<?php
/**
 * 微信相关
 */
namespace app\common;

use app\core\tool\Oss;
use app\lucky\user\model\UserV2Model;
use app\lucky\user\model\WechatUserModel;
use app\polymerize\user\context\user\LoginByTelContext;
use app\weapp\service\UserService;

class Wechat
{
    private $appId =  '';
    private $secret = '';

    use InstanceTrait;

    public function __construct($appId, $secret)
    {
        $this->appId = $appId;
        $this->secret = $secret;
    }

    public function getWeChat($state, $redirectUri)
    {
        $redirectUri = urlencode($redirectUri);
        $url = "https://open.weixin.qq.com/connect/qrconnect?appid={$this->appId}&redirect_uri={$redirectUri}&response_type=code&scope=snsapi_base&state={$state}#wechat_redirect";
        return ['code' => _MSG_SYSTEM_SUCCESS, 'data' => ['url' => $url], 'msg' => '成功'];
    }

    public function getWeChatCode($state, $redirectUri)
    {
        $redirectUri = urlencode($redirectUri);
        $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid={$this->appId}&redirect_uri={$redirectUri}&response_type=code&scope=snsapi_userinfo&state={$state}#wechat_redirect";
        return ['code' => _MSG_SYSTEM_SUCCESS, 'data' => ['url' => $url], 'msg' => '成功'];
    }

    /**
     * 根据code得到获取用户信息的accessToken
     * @param $code
     */
    public function getAccessTokenByCode($code)
    {
        //TODO 先取缓存，没有再去微信获取
        $cacheData = Redis::getInstance()->redisGet('wx:code_token:'. $code);
        if ($cacheData['code'] == _MSG_SYSTEM_SUCCESS) {
            return ['code' => _MSG_SYSTEM_SUCCESS, 'data' => $cacheData['data'], 'msg' => '获取微信授权失败'];
        }
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={$this->appId}&secret={$this->secret}&code={$code}&grant_type=authorization_code";
        $accessRes = file_get_contents($url);
        $accessRes = json_decode($accessRes, true);
        if (empty($accessRes['access_token'])) {
            \Log::error('微信获取accessToken失败' . var_export($accessRes, true));
            return ['code' => _MSG_SYSTEM_ERROR, 'data' => [], 'msg' => '获取微信授权失败'];
        }
        Redis::getInstance()->redisSet('wx:code_token:'. $code, $accessRes, 3600);
        return ['code' => _MSG_SYSTEM_SUCCESS, 'data' => $accessRes, 'msg' => '获取微信授权失败'];
    }

    /**
     * 用accessToken和openId从公众平台获取用户的基本信息
     * @param $accessToken
     * @param $openId
     * @return array
     */
    public function getWechatUserInfo($accessToken, $openId, $sys)
    {
        $url = "https://api.weixin.qq.com/sns/userinfo?access_token={$accessToken}&openid={$openId}&lang=zh_CN";
        $userInfo = file_get_contents($url);
        $userInfo = json_decode($userInfo, true);
        if (empty($userInfo['openid'])) {
            \Log::error('微信获取用户信息失败');
            return ['code' => _MSG_SYSTEM_ERROR, 'data' => [], 'msg' => '获取微信用户信息失败'];
        }
        //初始化wechatuser表
        $wechatInfo = WechatUserModel::getInstance()->getOneByOpenId($openId, $sys);
        WechatUserModel::getInstance()->initWechatUserV2($userInfo, $sys, $wechatInfo);
        return ['code' => _MSG_SYSTEM_SUCCESS, 'data' => $userInfo, 'msg' => '成功'];
    }

    /**
     * 授权码登陆用户
     * @param $code 授权码
     * @param string $identify //极光标识
     * @return array
     */
    public function loginByCode($code, $sys, $identify = '')
    {
        $retData = $this->getAccessTokenByCode($code);
        if ($retData['code'] != _MSG_SYSTEM_SUCCESS) {
            return $retData;
        }
        $accessRes = $retData['data'];
        $accessToken = $accessRes['access_token'];
        $openId = $accessRes['openid'];
        $this->getWechatUserInfo($accessToken, $openId, $sys);
        $userInfo = UserV2Model::getInstance()->getUserByOpenIdAndSys($openId, $sys);
        if (empty($userInfo)) {
            return ['code' => -2, 'data' => ['openid' => $openId, 'sys' => $sys], 'msg' => '用户未绑定手机号'];
        }
        $logData = [
            'tel' => $userInfo['tel'],
            'identify' => $identify,
            'openid' => $openId,
            'login_mode' => 'wx'
        ];
        $loginContext = new LoginByTelContext($logData);
        $res = UserService::getInstance()->loginByTel($loginContext, $sys);
        if ($res[0] != _MSG_SYSTEM_SUCCESS) {
            return ['code' => _MSG_SYSTEM_ERROR, 'data' => [], 'msg' => $res[1]];
        }
        return ['code' => _MSG_SYSTEM_SUCCESS, 'data' => $userInfo, 'msg' => '成功'];
    }


    /**
     * 获取JS签名
     * @return array   签名数组
     */
    public function getSignPackage($url = '')
    {
        $jsapiTicket = $this->getJsApiTicket();
        if (empty($url)) {
            $url = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
        }

        $timestamp = time();
        $nonceStr = "";
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        for ($i = 0; $i < 16; $i++) {
            $nonceStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";

        $signature = sha1($string);

        $signPackage = array(
            "jsapi_ticket" => $jsapiTicket,
            "appId" => $this->appId,
            "nonceStr" => $nonceStr,
            "timestamp" => $timestamp,
            "signature" => $signature,
        );
        return $signPackage;
    }

    /**
     * 获取jsapi_ticket
     * @return string   jsapi_ticket
     */
    private function getJsApiTicket()
    {
        $dev_jsapi = Redis::getInstance()->redisGet('wx:client:wx_jsapi_ticket'. $this->appId);
        if ($dev_jsapi['code'] == _MSG_SYSTEM_SUCCESS && !empty($dev_jsapi['data']['ticket'])) {
            return $dev_jsapi['data']['ticket'];
        }
        $accessToken = $this->getAccessToken();
        $param = array(
            'access_token' => $accessToken['access_token'],
            'type' => 'jsapi'
        );
        $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
        $js_ticket = self::http($url, $param);
        $ticket = json_decode($js_ticket, true);
        if (is_array($ticket)) {
            if ($ticket['errcode'] != 0) {
                throw new \Exception($ticket['errmsg']);
            } else {
                Redis::getInstance()->redisSet('wx:client:wx_jsapi_ticket'. $this->appId, $ticket, 1800);
                return $ticket['ticket'];
            }
        } else {
            throw new \Exception('获取微信jsapi_ticket失败！');
        }
    }

    /**
     * 发送HTTP请求方法，目前只支持CURL发送请求
     * @param  string $url    请求URL
     * @param  array  $param  GET参数数组
     * @param  array  $data   POST的数据，GET请求时该参数无效
     * @param  string $method 请求方法GET/POST
     * @return array          响应数据
     */
    protected static function http($url, $param, $data = '', $method = 'GET')
    {
        switch ($method) {
            case 'GET':
                $get_data = '';
                foreach ($param as $k => $v) {
                    $get_data .= "{$k}={$v}&";
                }
                $get_data = trim($get_data, '&');
                $url .= '?' . $get_data;
                $result = file_get_contents($url);
                return $result;
                break;
            case 'POST':
                $opts = array(
                    CURLOPT_TIMEOUT => 30,
                    CURLOPT_RETURNTRANSFER => 1,
                    CURLOPT_SSL_VERIFYPEER => false,
                    CURLOPT_SSL_VERIFYHOST => false,
                );
                /* 根据请求类型设置特定参数 */
                $opts[CURLOPT_URL] = $url . '?' . http_build_query($param);
//                if (strtoupper($method) == 'POST') {
                $opts[CURLOPT_POST] = 1;
                $opts[CURLOPT_POSTFIELDS] = $data;
                /* 初始化并执行curl请求 */
                $ch = curl_init();
                curl_setopt_array($ch, $opts);
                $data = curl_exec($ch);
                $error = curl_error($ch);
                curl_close($ch);
                //发生错误，抛出异常
                if ($error) {
                    throw new \Exception('请求发生错误：' . $error);
                }
                return $data;
                break;
            default:
                break;
        }
    }

    /**
     * 获取access_token，用于后续接口访问
     * @return array access_token信息，包含 token 和有效期
     */
    public function getAccessToken()
    {
        $accessTokenData = Redis::getInstance()->redisGet('wx:client:wx_access_token'. $this->appId);
        if (!empty($accessTokenData['access_token'])) {
            return $accessTokenData;
        }
        $param = array(
            'appid' => $this->appId,
            'secret' => $this->secret
        );
        $param['grant_type'] = 'client_credential';
        $url = "https://api.weixin.qq.com/cgi-bin/token";

        $access_token = self::http($url, $param);
        $token = json_decode($access_token, true);
        if (is_array($token)) {
            if (isset($token['errcode'])) {
                throw new \Exception($token['errmsg']);
            } else {
                Redis::getInstance()->redisSet('wx:client:wx_access_token'. $this->appId, $token, 3600);
                return $token;
            }
        } else {
            throw new \Exception('获取微信access_token失败！');
        }
    }
}
